﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Transactions;
using Abp.Auditing;
using Abp.AutoMapper;
using Abp.Domain.Uow;
using Abp.Extensions;
using Abp.Runtime.Security;
using Abp.Timing;
using Hicap.AuthorizationServer.Core.Common;
using Hicap.AuthorizationServer.Core.Models;

namespace Hicap.AuthorizationServer.Core.Managers.Authorization
{
    public class LogInManager : AuthorizationServerDomainServiceBase, ILogInManager
    {
        public IClientInfoProvider ClientInfoProvider { get; set; }
        private readonly IUserManager _userManager;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IUserRoleRelatedManager _userRoleRelatedManager;
        private readonly IAuthObjectRoleRelatedManager _authObjectRoleRelatedManager;

        public LogInManager(
            IUserManager userManager,
            IUserRoleRelatedManager userRoleRelatedManager,
            IAuthObjectRoleRelatedManager authObjectRoleRelatedManager,
        IUnitOfWorkManager unitOfWorkManager)
        {
            _userManager = userManager;
            _userRoleRelatedManager = userRoleRelatedManager;
            _authObjectRoleRelatedManager = authObjectRoleRelatedManager;
            _unitOfWorkManager = unitOfWorkManager;
        }

        [UnitOfWork]
        public virtual async Task<LoginResult> LoginAsync(string userCode, string plainPassword)
        {
            var result = await LoginAsyncInternal(userCode, plainPassword);
            await SaveLoginAttempt(result, userCode);
            return result;
        }

        private async Task<LoginResult> LoginAsyncInternal(string userCode, string plainPassword)
        {
            if (userCode.IsNullOrEmpty())
            {
                throw new ArgumentNullException(L("NullException{0}", nameof(userCode)));
            }

            if (plainPassword.IsNullOrEmpty())
            {
                throw new ArgumentNullException(L("NullException{0}", nameof(plainPassword)));
            }

            var user = await _userManager.FindByUserCode(userCode);
            if (user == null)
            {
                return new LoginResult(LoginResultType.InvalidUserCode);
            }

            if (!_userManager.CheckUserPassword(plainPassword, user))
            {
                return new LoginResult(LoginResultType.InvalidPassword, user);
            }

            return await CreateLoginSucceedResultAsync(user);
        }

        private async Task<LoginResult> CreateLoginSucceedResultAsync(User user)
        {
            user.LastLoginTime = Clock.Now;

            var principal = await CreatePrinciple(user);

            //  await _userRepository.UpdateAsync(user);
            await _userManager.UpdateUser(user);

            return new LoginResult(
                user,
                principal.Identity as ClaimsIdentity
            );
        }

        public Task<ClaimsPrincipal> CreatePrinciple(User user)
        {
            var identity = new ClaimsIdentity();
            identity.AddClaim(new Claim(AbpClaimTypes.UserName, user.UserCode));
            identity.AddClaim(new Claim(AbpClaimTypes.UserId, user.Id.ToString()));
            identity.AddClaim(new Claim(ClaimTypes.AuthorizationDecision, GetUserPermission(user.Id)));
            identity.AddClaim(new Claim(ClaimTypes.GroupSid, Convert.ToString(user.SubordinateCompany?.Id)));

            return Task.FromResult(new ClaimsPrincipal(identity));
        }

        protected virtual async Task SaveLoginAttempt(LoginResult loginResult, string userCode)
        {
            using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress))
            {
                var loginAttempt = new UserLoginAttempt
                {
                    UserId = loginResult.User != null ? loginResult.User.Id : (int?)null,
                    UserCode = userCode,

                    Result = loginResult.Result,
                    BrowserInfo = ClientInfoProvider.BrowserInfo,
                    ClientIpAddress = ClientInfoProvider.ClientIpAddress,
                    ClientName = ClientInfoProvider.ComputerName,
                };

                //   await _userLoginAttemptRepository.InsertAsync(loginAttempt);
                await _unitOfWorkManager.Current.SaveChangesAsync();

                await uow.CompleteAsync();
            }
        }

        public string GetUserPermission(int userId)
        {
            var userAuth = GetFromDatabase(userId);

            return String.Join(",", userAuth.AuthObjects.Select(x => x.ObjectCode));
        }

        private UserAuth GetFromDatabase(int id)
        {
            var result = new UserAuth();
            //... 从数据库中检索数据
            // var roles = _userRoleRelatedManager.GetQuery(x => x.Id == id).Include(x => x.Role);
            var roles = _userRoleRelatedManager.GetRole(id);
            //var auths = _authObjectRoleRelatedManager.GetQuery(x => roles.Select(y => y.RoleId).Any(z => z == x.RoleId)).Include(x => x.AuthObject)
            //.Select(x => x.AuthObject).ToList();
            var auths = _authObjectRoleRelatedManager.GetAuthRole(roles);

            result.Roles = roles.MapTo<List<Role>>();
            result.AuthObjects = auths.MapTo<List<AuthObject>>();
            result.User = _userManager.GetUserById(id);

            return result;
        }
    }
}